/*****************************************************
*                                                                          
*               COPYRIGHT (c) 2017-2019 Hargic Corporation                 
*                         All Rights Reserved                              
*                                                                          
* The source code contained or described herein and all documents          
* related to the source code ("Material") are owned by Hargic            
* Corporation or its licensors.  Title to the Material remains             
* with Hargic Corporation or its suppliers and licensors.                  
*                                                                          
* The Material is protected by worldwide copyright and trade secret        
* laws and treaty provisions. No part of the Material may be used,         
* copied, reproduced, modified, published, uploaded, posted, transmitted,  
* distributed, or disclosed in any way except in accordance with the       
* applicable license agreement.                                            
*                                                                          
* No license under any patent, copyright, trade secret or other            
* intellectual property right is granted to or conferred upon you by       
* disclosure or delivery of the Materials, either expressly, by            
* implication, inducement, estoppel, except in accordance with the         
* applicable license agreement.                                            
*                                                                          
* Unless otherwise agreed by Hargic in writing, you may not remove or      
* alter this notice or any other notice embedded in Materials by Hargic    
* or Hargic's suppliers or licensors in any way.                           
*                                                                          
** Author: shengda.huang
** Date: 2019-02-26
*
*****************************************************/

/***** Include files ********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <termios.h>
#include <pthread.h>
#include "uart_if.h"

/***** Defines **************************************/
#define uart_log(_fmt_, ...)          printf("[UART][%s:%d][%s] "_fmt_"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define uart_log_err(_fmt_, ...)      printf("[UART][ERR][%s:%d][%s] "_fmt_"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define UART_NUM_MAX	2

/***** Define structure *****************************/
typedef struct 
{
	int 			uartfd;
	int 			baud;
	int 			bits;
	int 			stop;
	char 			parity;
	unsigned char   task_alive:4;
	unsigned char 	port_opened:4;
	pthread_t       taskid;
	char 			*devname;
    int (*rx_cb)(unsigned char *pbuf, unsigned short maxsize);
} uart_mgr_t;

/***** Define constant ******************************/

/***** Define global ********************************/
uart_mgr_t uart_mgr[UART_NUM_MAX];

/***** Define Prototype of functions ****************/

static uart_mgr_t *uart_find_free_node(void)
{
	int i;
	for (i = 0; i < UART_NUM_MAX; i++)
	{
		if (uart_mgr[i].port_opened == 0)
			return &uart_mgr[i];
	}
	return NULL;
}

static uart_mgr_t *uart_find_mgr(int fd)
{
	int i;
	for (i = 0; i < UART_NUM_MAX; i++)
	{
		if (uart_mgr[i].port_opened != 0 && uart_mgr[i].uartfd == fd)
			return &uart_mgr[i];
	}
	return NULL;
}

/**
 * @brief
 * @param  None
 * @retval None
 */
int uart_open(char *devname)
{
    assert(devname);

    uart_mgr_t *_this = uart_find_free_node();
    if (_this == NULL)
    {
    	uart_log_err("uart mgr full");
    	return -1;
    }

    _this->uartfd = open(devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(_this->uartfd == -1)
    {
        uart_log_err("open %s failed", devname);
    }
    else
    {
    	_this->devname = devname;
    	_this->port_opened = !0;
    }

    return _this->uartfd;
}

static void uart_dump_setting(char *desc, struct termios *tio)
{
    uart_log("%s", desc);
    uart_log("c_iflag 0x%x", tio->c_iflag);
    uart_log("c_oflag 0x%x", tio->c_oflag);
    uart_log("c_cflag 0x%x", tio->c_cflag);
    uart_log("c_lflag 0x%x", tio->c_lflag);
}

int uart_setopt(int fd, int baud, int bits, char parity, int stop)
{
    struct termios newtio, oldtio;
    int ret;

    uart_mgr_t *_this = uart_find_mgr(fd);
    if (_this == NULL)
    {
    	uart_log_err("not find fd %d mgr", fd);
    	return -3;
    }
    _this->baud = baud;
    _this->bits = bits;
    _this->parity = parity;
    _this->stop = stop;

    if ((ret = tcgetattr(_this->uartfd, &oldtio)) != 0) 
    { 
        uart_log_err("%s tcgetattr failed %d", _this->devname, ret);
        return -1;
    }
    uart_dump_setting("tcgetattr", &oldtio);

    memset(&newtio, 0x0, sizeof(struct termios));

    /*
     * Enable the receiver and set local mode...
     */
    newtio.c_cflag |= CLOCAL | CREAD;
    
    /*
     *  Mask the character size bits
     *  Generally select 8 data bits
     */
    newtio.c_cflag &= ~CSIZE;
    switch (_this->bits)
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
        default:
            newtio.c_cflag |= CS8;
            uart_log("%s invalid bits %d should be 7 or 8, set default 8", _this->devname, _this->bits);
            break;
    }
 
    /*
     *  设置奇偶校验位
     */
    switch (_this->parity)
    {
        // 无奇偶校验位
        case 'n':
        case 'N':  
            newtio.c_cflag &= ~PARENB;  // 不产生奇偶位，执行奇偶校验
            newtio.c_iflag &= ~INPCK;   // 使奇偶校验不起作用
            break;
        // 设为空格,即停止位为2位
        case 's':
        case 'S':
            newtio.c_cflag &= ~PARENB;  // 不产生奇偶位，执行奇偶校验
            break;
        // setting odd parity
        case 'o':
        case 'O':
            newtio.c_cflag |= PARENB;   // 产生奇偶位，执行奇偶校验
            newtio.c_cflag |= PARODD;   // 若设置则为奇校验,否则为偶校验
            newtio.c_iflag |= INPCK;    // 使奇偶校验起作用
            newtio.c_iflag |= ISTRIP;   // 若设置则有效输入数字被剥离7个字节，否则保留全部8位
            break;
        // setting even parity
        case 'e':
        case 'E': 
            newtio.c_cflag |= PARENB;   // 产生奇偶位，执行奇偶校验
            newtio.c_cflag &= ~PARODD;  // 若设置则为奇校验,否则为偶校验
            newtio.c_iflag |= INPCK;    // 使奇偶校验起作用
            newtio.c_iflag |= ISTRIP;   // 若设置则有效输入数字被剥离7个字节，否则保留全部8位
            break;
        default:
            newtio.c_cflag &= ~PARENB;  // 不产生奇偶位，执行奇偶校验
            newtio.c_iflag &= ~INPCK;   // 使奇偶校验不起作用
            uart_log("%s invalid parity %c set default 'n'", _this->devname, _this->parity);
            break;
    }
 
    switch (_this->baud)
    {
        case 1200:
            cfsetispeed(&newtio, B1200);
            cfsetospeed(&newtio, B1200);
            break;
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
            break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
        case 19200:
            cfsetispeed(&newtio, B19200);
            cfsetospeed(&newtio, B19200);
            break;
        case 38400:
            cfsetispeed(&newtio, B38400);
            cfsetospeed(&newtio, B38400);
            break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;
        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            uart_log("%s baud only support 1200/2400/4800/9600/19200/38400/115200, set default 9600", _this->devname);
            break;
    }

    switch (_this->stop)
    {
        case 1:
            newtio.c_cflag &= ~CSTOPB;
            break;
        case 2:
            newtio.c_cflag |= CSTOPB;
            break;
        default:
            newtio.c_cflag &= ~CSTOPB;
            uart_log("%s invalid stop %d set default 1", _this->devname, _this->stop);
            break;
    }

    newtio.c_cc[VTIME]  = 1;    // 设置超时 0.1 seconds, unit: 100ms, set 0 will block forever
    newtio.c_cc[VMIN]   = 0;    // 指定读取的最小字符数

    // 清空输入和输出缓存
    tcflush(_this->uartfd,TCIFLUSH);

    // set the options
    if((ret = tcsetattr(_this->uartfd, TCSANOW, &newtio))!=0)
    {
        uart_log_err("%s tcsetattr failed %d", _this->devname, ret);
        return -2;
    }
    
    uart_log("%s setting success", _this->devname);
    return 0;
}

int uart_read(int fd, char *rd_buff, size_t len)
{
    return read(fd, rd_buff, len);
}

int uart_write(int fd, const char *wr_buff, size_t len)
{
    return write(fd, wr_buff, len);
}

int uart_close(int fd)
{
	uart_mgr_t *_this = uart_find_mgr(fd);
    if (_this == NULL)
    {
    	uart_log_err("not find fd %d mgr", fd);
    	return -3;
    }
    assert(_this->uartfd);
    close(_this->uartfd);
    _this->port_opened = 0;
    return 0;
}

static void *uart_rx_polling_thread(void *arg)
{
    uart_mgr_t *mgr = (uart_mgr_t *)arg;
    unsigned char buffer[1024];
    short bufflen;

    while (mgr->task_alive)
    {
        bufflen = read(mgr->uartfd, buffer, sizeof(buffer));
        if (bufflen > 0 && mgr->rx_cb != NULL)
            (*mgr->rx_cb)(buffer, bufflen);
        usleep(1000);
    }
    mgr->task_alive = 0;
    uart_log("%s exit", mgr->devname);
    return NULL;
}

int uart_start_rx_background(int fd, int (*rxcb)(unsigned char *recv, unsigned short len))
{
	uart_mgr_t *_this = uart_find_mgr(fd);
    if (_this == NULL)
    {
    	uart_log_err("not find fd %d mgr", fd);
    	return -3;
    }

    _this->task_alive = !0;
    _this->rx_cb = rxcb;
    int ret = pthread_create(&_this->taskid, NULL, uart_rx_polling_thread, _this);
    if (ret != 0)
    {
        uart_log_err("create %s rx background failed %d", _this->devname, ret);
        _this->task_alive = 0;
        return -1;
    }
    uart_log("create %s rx background success", _this->devname);
    return 0;
}

int uart_stop_rx_background(int fd)
{
	uart_mgr_t *_this = uart_find_mgr(fd);
    if (_this == NULL)
    {
    	uart_log_err("not find fd %d mgr", fd);
    	return -3;
    }
    _this->task_alive = 0;
    pthread_join(_this->taskid, NULL);
    uart_log("%s exit", _this->devname);
    return 0;
}

/* End of file */



